/*
 * Accordion 1.5 - jQuery menu widget
 *
 * Copyright (c) 2007 Jörn Zaefferer, Frank Marcia
 *
 * http://bassistance.de/jquery-plugins/jquery-plugin-accordion/
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Revision: $Id: jquery.accordion.js 2951 2007-08-28 07:21:13Z joern.zaefferer $
 *
 */

(function($) {

    $.ui = $.ui || {}

    $.ui.accordion = {};
    $.extend($.ui.accordion, {
        defaults: {
            selectedClass: "selected",
            alwaysOpen: true,
            animated: 'slide',
            event: "click",
            header: "a"
        },
        animations: {
            slide: function(settings, additions) {
                settings = $.extend({
                    easing: "swing",
                    duration: 300
                }, settings, additions);
                if (!settings.toHide.size()) {
                    settings.toShow.animate({height: "show"}, {
                        duration: settings.duration,
                        easing: settings.easing,
                        complete: settings.finished
                    });
                    return;
                }
                var hideHeight = settings.toHide.height(),
                        showHeight = settings.toShow.height(),
                        difference = showHeight / hideHeight;
                settings.toShow.css({ height: 0, overflow: 'hidden' }).show();
                settings.toHide.filter(":hidden").each(settings.finished).end().filter(":visible").animate({height:"hide"}, {
                    step: function(n) {
                        settings.toShow.height(Math.ceil((hideHeight - (n)) * difference));
                    },
                    duration: settings.duration,
                    easing: settings.easing,
                    complete: settings.finished
                });
            },
            bounceslide: function(settings) {
                this.slide(settings, {
                    easing: settings.down ? "bounceout" : "swing",
                    duration: settings.down ? 1000 : 200
                });
            },
            easeslide: function(settings) {
                this.slide(settings, {
                    easing: "easeinout",
                    duration: 700
                })
            }
        }
    });

    $.fn.extend({
        nextUntil: function(expr) {
            var match = [];
	
	    // We need to figure out which elements to push onto the array
            this.each(function() {
                // Traverse through the sibling nodes
                for (var i = this.nextSibling; i; i = i.nextSibling) {
                    // Make sure that we're only dealing with elements
                    if (i.nodeType != 1) continue;
	
	            // If we find a match then we need to stop
                    if ($.filter(expr, [i]).r.length) break;
	
	            // Otherwise, add it on to the stack
                    match.push(i);
                }
            });

            return this.pushStack(match);
        },
        // the plugin method itself
        accordion: function(settings) {
            if (!this.length)
                return this;
	
		// setup configuration
            settings = $.extend({}, $.ui.accordion.defaults, settings);

            if (settings.navigation) {
                var current = this.find("a").filter(function() {
                    return this.href == location.href;
                });
                if (current.length) {
                    if (current.filter(settings.header).length) {
                        settings.active = current;
                    } else {
                        settings.active = current.parent().parent().prev();
                        current.addClass("current");
                    }
                }
            }
		
		// calculate active if not specified, using the first header
            var container = this,
                    headers = container.find(settings.header),
                    active = findActive(settings.active),
                    running = 0;

            if (settings.fillSpace) {
                var maxHeight = this.parent().height();
                headers.each(function() {
                    maxHeight -= $(this).outerHeight();
                });
                var maxPadding = 0;
                headers.nextUntil(settings.header).each(function() {
                    maxPadding = Math.max(maxPadding, $(this).innerHeight() - $(this).height());
                }).height(maxHeight - maxPadding);
            } else if (settings.autoheight) {
                var maxHeight = 0;
                headers.nextUntil(settings.header).each(function() {
                    maxHeight = Math.max(maxHeight, $(this).height());
                }).height(maxHeight);
            }

            headers
                    .not(active || "")
                    .nextUntil(settings.header)
                    .hide();
            active.parent().andSelf().addClass(settings.selectedClass);


            function findActive(selector) {
                return selector != undefined
                        ? typeof selector == "number"
                        ? headers.filter(":eq(" + selector + ")")
                        : headers.not(headers.not(selector))
                        : selector === false
                        ? $("<div>")
                        : headers.filter(":eq(0)");
            }

            function toggle(toShow, toHide, data, clickedActive, down) {
                var finished = function(cancel) {
                    running = cancel ? 0 : --running;
                    if (running)
                        return;
				// trigger custom change event
                    container.trigger("change", data);
                };
			
			// count elements to animate
                running = toHide.size() == 0 ? toShow.size() : toHide.size();

                if (settings.animated) {
                    if (!settings.alwaysOpen && clickedActive) {
                        toShow.slideToggle(settings.animated);
                        finished(true);
                    } else {
                        $.ui.accordion.animations[settings.animated]({
                            toShow: toShow,
                            toHide: toHide,
                            finished: finished,
                            down: down
                        });
                    }
                } else {
                    if (!settings.alwaysOpen && clickedActive) {
                        toShow.toggle();
                    } else {
                        toHide.hide();
                        toShow.show();
                    }
                    finished(true);
                }
            }

            function clickHandler(event) {
                // called only when using activate(false) to close all parts programmatically
                if (!event.target && !settings.alwaysOpen) {
                    active.toggleClass(settings.selectedClass);
                    var toHide = active.nextUntil(settings.header);
                    var toShow = active = $([]);
                    toggle(toShow, toHide);
                    return;
                }
			// get the click target
                var clicked = $(event.target);
			
			// due to the event delegation model, we have to check if one
                // of the parent elements is our actual header, and find that
                if (clicked.parents(settings.header).length)
                    while (!clicked.is(settings.header))
                        clicked = clicked.parent();

                var clickedActive = clicked[0] == active[0];
			
			// if animations are still active, or the active header is the target, ignore click
                if (running || (settings.alwaysOpen && clickedActive) || !clicked.is(settings.header))
                    return;

			// switch classes
                active.parent().andSelf().toggleClass(settings.selectedClass);
                if (!clickedActive) {
                    clicked.parent().andSelf().addClass(settings.selectedClass);
                }

			// find elements to show and hide
                var toShow = clicked.nextUntil(settings.header),
                        toHide = active.nextUntil(settings.header),
                        data = [clicked, active, toShow, toHide],
                        down = headers.index(active[0]) > headers.index(clicked[0]);

                active = clickedActive ? $([]) : clicked;
                toggle(toShow, toHide, data, clickedActive, down);

                return !toShow.length;
            }
            ;
            function activateHandler(event, index) {
                // IE manages to call activateHandler on normal clicks
                if (arguments.length == 1)
                    return;
			// call clickHandler with custom event
                clickHandler({
                    target: findActive(index)[0]
                });
            }
            ;

            return container
                    .bind(settings.event, clickHandler)
                    .bind("activate", activateHandler);
        },
        activate: function(index) {
            return this.trigger('activate', [index]);
        }
    });

})(jQuery);